#include "util.hpp"
#include<filesystem>

const std::string contenttype("Content-Type"),
keepalive("Keep-Alive"),truestr("true"),textcss("text/css"),
texthtml("text/html;charset=utf-8"),contentlength("Content-Length"),
charsetutf8("charset=utf-8"),semicol(";"),GET("GET"),OK("OK"),
contentencoding("Content-Encoding"),chunked("chunked"),
connection("Connection"),lowerkeepalive("keep-alive"),
appjson("application/json;charset=utf-8"),textplain("text/plain;charset=utf-8"),
statusok("OK"),deflate("deflate"),http1_1("HTTP/1.1"),gzip("gzip"),zlib("zlib");

const std::unordered_map<std::string,CompressFunction> supportcompress{
	{gzip,libdeflate_gzip_compress},{deflate,libdeflate_deflate_compress},
	{zlib,libdeflate_zlib_compress}
};
const std::unordered_map<std::string,DecompressFunction> supportdecompress{
	{gzip,libdeflate_gzip_decompress},{deflate,libdeflate_deflate_decompress},
	{zlib,libdeflate_zlib_decompress}
};

const std::unordered_map<std::string,std::string> supportformat{
	{".jpg","image/jpeg"},{".html",texthtml},{".png","image/png"},
	{".txt",textplain},{".json",appjson},{".pdf","application/pdf"},
	{".htm",texthtml},{".css",textcss}
};

Memory::~Memory(){if(autofree&&data)free(data);}
static std::string emptystring,onedot("."),twodots("..");
Dir::~Dir(){
	auto& m=children;
	m.erase(emptystring);
	m.erase(onedot);
	m.erase(twodots);
	for(auto& v:m)delete v.second;
}
Resource* Dir::get(const char* name){
	auto f=children.find(name);
	if(f!=children.end())return f->second;
	return nullptr;
}
Dir::Dir(const char* p,const char* n,const std::string& compressmethod):
Resource(n,compressmethod){
	#ifdef WIN32
	using namespace std::__fs;
	#else
	using namespace std;
	#endif
	auto path=filesystem::path(p);
	auto& m=children;
	m.emplace(emptystring,this);
	m.emplace(onedot,this);
	if(filesystem::exists(path)){
		filesystem::directory_iterator l(p);
		for(auto& iter:l){
			auto& path=iter.path();
			filesystem::directory_entry cur(path.string().c_str());
			if(cur.status().type()==filesystem::file_type::directory){
				auto v=new Dir(path.string().c_str(),
				path.filename().u8string().c_str(),compressmethod);
				m.emplace(path.filename().u8string(),v);
				v->children.emplace(twodots,this);
				continue;
			}
			m.emplace(path.filename().u8string(),
			new File(path.string().c_str(),path.filename().u8string().
			substr(0,path.filename().u8string().find('.')).c_str(),
			path.extension().u8string().c_str(),compressmethod));
		}
		return;
	}
	printf("Error:dir %s not exists\n",p);
}
unsigned int Dir::type(){return res_dir;}
void Dir::print(){
	for(auto& v:children){
		auto& first=v.first;
		auto c=v.second;
		if(!c->type()){
			if(first!=emptystring&&first!=onedot&&first!=twodots){
				printf("/");
				((Dir*)c)->print();
			}
			continue;
		}
		printf("\n");
	}
}

struct libdeflate_compressor* compressor=nullptr;
struct libdeflate_decompressor* decompressor=nullptr;

File::~File(){}
unsigned int File::type(){return res_file;}
Resource* File::get(const char* name){return nullptr;}

File::File(const char* path,const char* n,const char* e,
const std::string& compressmethod):Resource(n,compressmethod),
extension(e){
	auto fp=fopen(path,"rb");
	if(fp){
		fseeko64(fp,0,SEEK_END);
		memory.size=ftell(fp);
		memory.data=(char*)malloc(memory.size+9);//给压缩预留+9空间
        if(!memory.data){
            fclose(fp);
            printf("no enough memory for file %s(size:%zu)\n",path,memory.size);
            throw std::bad_alloc();
        }
		memory.autofree=true;
		rewind(fp);
		fread(memory.data,memory.size,1,fp);
		fclose(fp);
		auto before=memory.size;
		compress(memory.data,before,memory.data,&memory.size,compressmethod);
		if(memory.size!=before)memory.data=(char*)realloc(memory.data,memory.size);
		return;
	}
	printf("Error:%s not exists\n",path);
}
void File::compress(void* src,size_t srcsize,void* dest,size_t* destsize,
const std::string& method){
	auto f=supportcompress.find(method);
	if(f!=supportcompress.end()){
		*destsize=f->second(compressor,src,srcsize,dest,*destsize);
	}
}

Resource::~Resource(){}
Resource::Resource(const char* n,const std::string& compressmethod):name(n),
compressmethod(compressmethod){}
void Resource::compress(void* src,size_t srcsize,void* dest,size_t* destsize,
const std::string& method){}

bool StringTable::containkeyvalue(const std::string& k,const std::string& v){
	auto f=find(k);
	if(f!=end())return v==f->second;
	return false;
}
StringTable::StringTable(std::initializer_list<std::pair<const std::string&,
const std::string&>> v){
	reserve(v.size());
	for(auto& p:v)emplace(p.first,p.second);
}
StringTable::StringTable(){}

bool openutil(){
	compressor=libdeflate_alloc_compressor(12);
	decompressor=libdeflate_alloc_decompressor();
	if(compressor){
		if(decompressor)return true;
		libdeflate_free_compressor(compressor);
	}
	return false;
}
void closeutil(){
	libdeflate_free_compressor(compressor);
	libdeflate_free_decompressor(decompressor);
}